package com.bizmda.bizsip.sink.processor;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONObject;
import com.bizmda.bizsip.common.BizException;
import com.bizmda.bizsip.common.BizResultEnum;
import com.bizmda.bizsip.common.BizTools;
import com.bizmda.bizsip.common.BizUtils;
import com.bizmda.bizsip.config.AbstractSinkConfig;
import com.bizmda.bizsip.converter.Converter;
import com.bizmda.bizsip.sink.connector.Connector;
import com.bizmda.bizsip.sink.api.AbstractSinkService;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class BeanSinkProcessor extends AbstractSinkProcessor {
    private String clazzName;
    private Object clazz;
    private Map<String, Method> methodMap = new HashMap<>();
    private Converter converter;
    private Connector connector;

    public BeanSinkProcessor(AbstractSinkConfig sinkConfig) {
        super(sinkConfig);
        this.clazzName = sinkConfig.getClassName();
    }

    @Override
    JSONObject process(JSONObject jsonObject) throws BizException {
        String jsonClazzName;
        if (this.clazzName == null) {
            jsonClazzName = (String) jsonObject.get("className");
        } else {
            jsonClazzName = this.clazzName;
        }

        String jsonMethodName = (String) jsonObject.get("methodName");

        if (jsonClazzName == null) {
            throw new BizException(BizResultEnum.OTHER_JAVA_CLASS_METHOD_ERROR, "类名为NULL");
        }

        if (jsonMethodName == null) {
            throw new BizException(BizResultEnum.OTHER_JAVA_CLASS_METHOD_ERROR, "方法名为NULL");
        }
        log.debug("调用Sink服务[{}](处理器类型bean:{}.{}())",
                this.sinkConfig.getId(), jsonClazzName, jsonMethodName);
        if (this.clazz == null) {
            try {
                if (StrUtil.isEmpty(jsonClazzName)) {
                    throw new BizException(BizResultEnum.SINK_CLASSNAME_NOT_SET);
                }
                this.clazz = SpringUtil.getBean(Class.forName(jsonClazzName));
                if (this.clazz instanceof AbstractSinkService) {
                    AbstractSinkService sinkService = (AbstractSinkService) this.clazz;
                    sinkService.setConnector(this.connector);
                    sinkService.setConverter(this.converter);
                }
            } catch (ClassNotFoundException e) {
                log.error("Sink服务关联类不存在!",e);
                throw new BizException(BizResultEnum.CONNECTOR_JAVA_CLASS_CREATE_ERROR, e);
            }
        }
        Method method = this.methodMap.get(jsonMethodName);
        if (method == null) {
            try {
                method = ReflectUtil.getMethodByName(Class.forName(jsonClazzName), jsonMethodName);
            } catch (ClassNotFoundException e) {
                log.error("Sink服务关联类不存在!",e);
                throw new BizException(BizResultEnum.CONNECTOR_JAVA_CLASS_CREATE_ERROR, e);
            }
            this.methodMap.put(jsonMethodName, method);
        }
        Object returnValue;
        try {
            Object[] args = BizTools.convertJsonObject2MethodParameters(method, jsonObject.get("params"), (JSONObject) jsonObject.get("paramsTypes"));
            if (method.getParameterCount() == 0) {
                returnValue = method.invoke(clazz);
            } else if (method.getParameterCount() == 1) {
                log.trace("bean处理器类型Sink服务调用参数:{}",args[0]);
                returnValue = method.invoke(clazz, args[0]);
            } else {
                log.trace("bean处理器类型Sink服务调用参数:{}",args);
                returnValue = method.invoke(clazz, args);
            }
        } catch (IllegalAccessException e) {
            log.error("执行Sink服务相关类出错", e);
            throw new BizException(BizResultEnum.OTHER_JAVA_CLASS_METHOD_ERROR, e);
        } catch (InvocationTargetException e) {
            log.error("执行Sink服务相关类出错", e);
            Throwable t = e.getTargetException();
            if (t instanceof BizException) {
                throw (BizException) t;
            } else if (t instanceof UndeclaredThrowableException) {
                Throwable t1 = ((UndeclaredThrowableException) t).getUndeclaredThrowable();
                if (t1 instanceof BizException) {
                    throw (BizException) t1;
                }
            }
            throw new BizException(BizResultEnum.OTHER_JAVA_CLASS_METHOD_ERROR, e);
        }

        JSONObject jsonObject1 = new JSONObject();

        jsonObject1.set("result", BizTools.methodReturnBean2Json(returnValue));
        log.trace("Sink服务响应报文:", BizUtils.buildJsonLog(jsonObject1));
        return jsonObject1;
    }
}
